/*
 * SiFiSy - A File System Simulator
 * Copyright (C) 2010  SevenSoft
 *
 * This file is part of SiFiSy.
 *
 * SiFiSy is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * SiFiSy is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with SiFiSy.  If not, see <http://www.gnu.org/licenses/>.
 */
package sifisy.core;

import org.jdom.*;
import java.util.*;

/**
 * Simulation is the abstract class that represent all the common features of
 * all type of simulation.
 * @author Alessandro Vedovato
 * @version %I%, %G%
 * @createData 18/02/2010
 */
public abstract class Simulation {

    private String simName;
    Vector<File> files = new Vector<File>();
    Vector<Section> usedBlocks = new Vector<Section>();
    private int partitionDimension;
    private int partitionExponent;
    private int blockDimension;
    private int blockExponent;
    private int addressDimension;
    private int fileAllocationType;
    private int allocationNextIndex;
    private int rotationSpeed;
    private int trackPercent;
    private int seekTime;
//    private int trackSectors;
    private long maxLenghtFile;
    private double internalFragmentation;
    private double inflationIndex;
    private int averageReading;
    private double sizeFileSystem;
    private boolean modifiedCamps;
//    private boolean justCalculated;
    private boolean chooseAdvanced;

//   public Simulation(){
//       files = new Vector<File>();
//       usedBlocks = new Vector<Section>();
//   }
    /**
     * Create a new simulation
     */
    public Simulation() {
        // risultati inizializzati a -1
        setMaxLenghtFile(-1);
        setInternalFragmentation(-1);
        setInflationIndex(-1);
        setAverageReading(-1);
    }

    /**
     * Set the main fields of a simulation inside the XML tree
     * @param simulationRoot
     */
    void save(Element simulationRoot) {
        //simulationRoot.setAttribute(new Attribute("id", "0"));
        simulationRoot.addContent("\n    ");
        simulationRoot.addContent(new Element("simName").addContent(simName));
        simulationRoot.addContent("\n    ");
        simulationRoot.addContent(new Element("partitionDimension").addContent(String.valueOf(partitionDimension)));
        simulationRoot.addContent("\n    ");
        simulationRoot.addContent(new Element("partitionExponent").addContent(String.valueOf(partitionExponent)));
        simulationRoot.addContent("\n    ");
        simulationRoot.addContent(new Element("blockDimension").addContent(String.valueOf(blockDimension)));
        simulationRoot.addContent("\n    ");
        simulationRoot.addContent(new Element("blockExponent").addContent(String.valueOf(blockExponent)));
        simulationRoot.addContent("\n    ");
        simulationRoot.addContent(new Element("addressDimension").addContent(String.valueOf(addressDimension)));
        simulationRoot.addContent("\n    ");

        switch (fileAllocationType) {
            case 0:
                simulationRoot.addContent(new Element("fileAllocationType").addContent("First-Fit"));
                break;

            //irst-fit
            case 1:
                simulationRoot.addContent(new Element("fileAllocationType").addContent("Best-Fit"));
                break;

            //best-fit
            case 2:
                simulationRoot.addContent(new Element("fileAllocationType").addContent("Worst-Fit"));
                break;

            //worst-fit
            case 3:
                simulationRoot.addContent(new Element("fileAllocationType").addContent("Next-Fit"));
                break;

            //next-fit
        }
        //simulationRoot.addContent(new Element("fileAllocationType").
        //        addContent(String.valueOf(fileAllocationType)));
        simulationRoot.addContent("\n    ");
        simulationRoot.addContent(new Element("diskRotation").addContent(String.valueOf(rotationSpeed)));
        simulationRoot.addContent("\n    ");
        simulationRoot.addContent(new Element("trackPercent").addContent(String.valueOf(trackPercent)));
        simulationRoot.addContent("\n    ");
        simulationRoot.addContent(new Element("allocationNextIndex").addContent(String.valueOf(allocationNextIndex)));
        simulationRoot.addContent("\n    ");
        simulationRoot.addContent(new Element("timePosition").addContent(String.valueOf(seekTime)));
        simulationRoot.addContent("\n    ");
//        simulationRoot.addContent(new Element("trackSectors").
//                addContent(String.valueOf(trackSectors)));
        simulationRoot.addContent("\n    ");
        simulationRoot.addContent(new Element("maxLenghtFile").addContent(String.valueOf(maxLenghtFile)));
        simulationRoot.addContent("\n    ");
        simulationRoot.addContent(new Element("internalFragmentation").addContent(String.valueOf(internalFragmentation)));
        simulationRoot.addContent("\n    ");
        simulationRoot.addContent(new Element("inflationIndex").addContent(String.valueOf(inflationIndex)));
        simulationRoot.addContent("\n    ");
        simulationRoot.addContent(new Element("averageReading").addContent(String.valueOf(averageReading)));
        simulationRoot.addContent("\n    ");
        //simulationRoot.addContent(new Element("sizeFileSystem").
        //        addContent(String.valueOf(sizeFileSystem)));
        simulationRoot.addContent("\n    ");
        //simulationRoot.addContent(new Element("fileDimension").
        //      addContent(String.valueOf(fileDimension)));
        simulationRoot.addContent("\n    ");
        //simulationRoot.addContent(new Element("fileExponent").
        //      addContent(String.valueOf(fileExponent)));
        for (int i = 0; i < files.size(); i++) {
            simulationRoot.addContent("\n    ");
            Element file = new Element("file");
            file.addContent("\n        ");
            file.addContent(new Element("dimension").addContent(String.valueOf(files.elementAt(i).getDimension())));
            file.addContent("\n        ");
            file.addContent(new Element("exponent").addContent(String.valueOf(files.elementAt(i).getDimension())));
            file.addContent("\n        ");
            file.addContent(new Element("id").addContent(String.valueOf(files.elementAt(i).getId())));
            file.addContent("\n        ");
            file.addContent(new Element("size").addContent(String.valueOf(files.elementAt(i).getSize())));
            file.addContent("\n        ");
            file.addContent(new Element("averageBlocks").addContent(String.valueOf(files.elementAt(i).getAverageBlocks())));
            file.addContent("\n        ");
            file.addContent(new Element("selectedAdvanced").addContent(String.valueOf(files.elementAt(i).getSelectedAdvanced())));
            file.addContent("\n    ");
            simulationRoot.addContent(file);
        }
        for (int i = 0; i < usedBlocks.size(); i++) {
            simulationRoot.addContent("\n    ");
            Element section = new Element("Section");
            section.addContent("\n        ");
            section.addContent(new Element("id").addContent(String.valueOf(usedBlocks.elementAt(i).
                    getId())));
            section.addContent("\n        ");
            section.addContent(new Element("first").addContent(String.valueOf(usedBlocks.elementAt(i).
                    getFirst())));
            section.addContent("\n        ");
            section.addContent(new Element("lenght").addContent(String.valueOf(usedBlocks.elementAt(i).
                    getLenght())));
            section.addContent("\n    ");
            simulationRoot.addContent(section);
        }
    }

    /**
     * Invokes the proper method of allocation chosen by the parameter
     * fileAllocationType
     * @param section
     */
    void single(Section item) {
        if (usedBlocks.size() == 0) {
            usedBlocks.addElement(item);
        } else {
            long first = usedBlocks.lastElement().getFirst() + usedBlocks.lastElement().getLenght() + calculateRandomSpace();
            item.setFirst(first);
            usedBlocks.addElement(item);
        }
    }

    /**
     * Select the allocation policy
     * @param section section of a file
     */
    void insParts(Section section) {
        switch (fileAllocationType) {
            case 0:
                firstFit(section);
                break;
            case 1:
                bestFit(section);
                break;
            case 2:
                worstFit(section);
                break;
            case 3:
                nextFit(section);
                break;
            case 4:
                single(section);
                break;
        }
    }

    /**
     * Returns a value of blocks supposedly used that is used to separate
     * blocks of the same file and simulate fragmentation
     * @return int
     */
    long calculateRandomSpace() {
        long randomSpace = 0;
        long fileSpace = (files.firstElement().getSize()) / (long) (blockDimension * Math.pow(2, blockExponent));
        long aviableSpace = (calculateBlockNumber() - fileSpace) / 2;
        randomSpace = (long) ((Math.random() * 100) % aviableSpace);
        if (fileSpace == 0) {
            randomSpace = 1;
        } else {
            randomSpace = randomSpace / fileSpace;
            if (randomSpace == 0) {
                randomSpace = randomSpace + 1;
            }
        }
        return randomSpace;
    }

    /**
     *Calculate the values of a simulation
     */
    abstract void start();

    /**
     * Open an existing simulation
     * @param simulationRoot
     */
    void open(Element simulationRoot) {
        simName = ((Element) (simulationRoot.getChildren("simName").
                get(0))).getText();
        partitionDimension = Integer.parseInt(((Element) (simulationRoot.getChild("partitionDimension"))).getText());
        partitionExponent = Integer.parseInt(((Element) (simulationRoot.getChild("partitionExponent"))).getText());
        blockDimension = Integer.parseInt(((Element) (simulationRoot.getChild("blockDimension"))).getText());
        blockExponent = Integer.parseInt(((Element) (simulationRoot.getChild("blockExponent"))).getText());
        addressDimension = Integer.parseInt(((Element) (simulationRoot.getChild("addressDimension"))).getText());
        allocationNextIndex = Integer.parseInt(((Element) (simulationRoot.getChild("allocationNextIndex"))).getText());
        rotationSpeed = Integer.parseInt(((Element) (simulationRoot.getChild("diskRotation"))).getText());
        trackPercent = Integer.parseInt(((Element) (simulationRoot.getChild("trackPercent"))).getText());
        seekTime = Integer.parseInt(((Element) (simulationRoot.getChild("timePosition"))).getText());
//        trackSectors = Integer.parseInt(((Element)(simulationRoot.
//                getChild("trackSectors"))).getText());
        try {
            maxLenghtFile = Integer.parseInt(((Element) (simulationRoot.getChild("maxLenghtFile"))).getText());
        } catch (NumberFormatException e) {
            System.err.println("EXCEPTION: Simulation.maxLengthFile \"" + e.getMessage() + "\"");
            maxLenghtFile = 0;
        }
        try {
            internalFragmentation = new Double(((Element) (simulationRoot.getChild("internalFragmentation"))).getText()).doubleValue();
        } catch (NumberFormatException e) {
            System.err.println("EXCEPTION: Simulation.internalFragmentation \"" + e.getMessage() + "\"");
            internalFragmentation = 0;
        }
        try {
            inflationIndex = new Double(((Element) (simulationRoot.getChild("inflationIndex"))).getText()).doubleValue();
        } catch (NumberFormatException e) {
            System.err.println("EXCEPTION: Simulation.inflationIndex \"" + e.getMessage() + "\"");
            inflationIndex = 0;
        }
        try {
            averageReading = Integer.parseInt(((Element) (simulationRoot.getChild("averageReading"))).getText());
        } catch (NumberFormatException e) {
            System.err.println("EXCEPTION: Simulation.averageReading \"" + e.getMessage() + "\"");
            averageReading = 0;
        }
        //sizeFileSystem = Integer.parseInt(((Element)(simulationRoot.
        //        getChild("averageReading"))).getText());

        //fileDimension = Integer.parseInt(((Element)(simulationRoot.                getChild("fileDimension"))).getText());
        //fileExponent = Integer.parseInt(((Element)(simulationRoot.                getChild("fileExponent"))).getText());
        List filesRoot = simulationRoot.getChildren("file");
        for (int i = 0; i < filesRoot.size(); i++) {
            Element file = (Element) (filesRoot.get(i));
            File newFile = new File();
            newFile.setId(file.getChild("id").getText());
            newFile.setDimension(Integer.parseInt(file.getChild("dimension").getText()));
            newFile.setExponent(Integer.parseInt(file.getChild("exponent").getText()));
            newFile.setAverageBlocks(Integer.parseInt(file.getChild("averageBlocks").getText()));
            if (file.getChild("selectedAdvanced").getText().equals("false")) {
                newFile.setSelectedAdvanced(false);
            } else if (file.getChild("selectedAdvanced").getText().equals("true")) {
                newFile.setSelectedAdvanced(true);
            }
            files.add(newFile);
        }
        /*        List sectionsRoot = simulationRoot.getChildren("section");
        for(int i = 0; i < sectionsRoot.size(); i++) {
        Element section = (Element)(sectionsRoot.get(i));
        Section newSection = new Section();
        newSection.setId(section.getChild("id").getText());
        newSection.setFirst(Integer.parseInt(section.getChild("first").getText()));
        newSection.setLenght(Integer.parseInt(section.getChild("lenght").getText()));
        usedBlocks.add(newSection);
        }*/
        modifiedCamps = false;
//        justCalculated = true;
    }

    /**
     * Insert a file into the Vector files and insert into the Vector usedBlock
     * all sections of the inserted file
     * @param item
     */
    void addFile(File file) {
        files.add(file);
        breakFile(file);
    }

    /**
     * Breaks the file into sections
     * @param item
     */
    void breakFile(File item) {
        String fileId = item.getId();
        long SectionLenght = item.getAverageBlocks();
        long fileLenght = item.getSize();
        long blockSize = (long) (blockDimension * Math.pow(2, blockExponent));
        long blockNedded = fileLenght / blockSize;

        if (fileLenght % blockSize != 0) {
            blockNedded = blockNedded + 1;
        }
        while (blockNedded > 0) {
            Section section = new Section();
            blockNedded = blockNedded - SectionLenght;
            if (blockNedded < 0) {
                SectionLenght = SectionLenght + blockNedded;
            }
            section.setId(fileId);
            section.setLenght(SectionLenght);
            insParts(section);
        }
    }

    /**
     * Delete a file into the Vector files and delete from Vector usedBlock all
     * sections of the deleted file
     * @param item
     */
    void deleteFile(String ID) {
        File item = files.firstElement();
        boolean find = false;
        for (int index = 0; index < files.size() && !find; index++) {
            if (ID.equals(files.elementAt(index).getId())) {
                item = files.elementAt(index);
                find = true;
            }
        }
        files.remove(item);
        deleteParts(item);
    }

    /* Vediamo se riusciamo a metterlo o decidiamo di non metterlo
    public void modifyFile(){

    }*/
    /**
     * Delete from Vector usedBlock all sections of the file
     * @param item
     */
    void deleteParts(File item) {
        String identification = item.getId();
        int blocksNumber = usedBlocks.size();
        for (int index = 0; index < blocksNumber; index++) {
            String fileName = usedBlocks.get(index).getId();
            if (identification.equals(fileName)) {
                usedBlocks.remove(index);
                index--;
                blocksNumber--;
            }
        }
    }

    /**
     * abstract method, implementation calculate max file inserted in hard disk
     */
    abstract void viewMaxFile();

    /*
    public void calculateAddressDimension() {
    //ip che partDimension e blockDimension siano potenze di due, quindi la
    //loro divisione intera restituisce un intero.(e che siano espressi
    //in bytes) Long x = Double.doubleToRawLongBits(partDimension);
    String stringBinaryPartition = Long.toBinaryString(partitionDimension);
    System.out.println(stringBinaryPartition);
    String stringBinaryBlock = Long.toBinaryString(blockDimension);
    System.out.println(stringBinaryBlock);
    int binaryPartition = stringBinaryPartition.length();
    int binaryBlock = stringBinaryBlock.length();
    binaryBlock = binaryBlock + blockExponent;
    binaryPartition = binaryPartition + partitionExponent;
    int address = binaryPartition - binaryBlock;
    System.out.println("binaryPartition: " + binaryPartition + " binaryBlock: " + binaryBlock);

    addressDimension = getNearestTwoPow(address);
    //per ip almeno 8, voglio che address sia espresso in Byte
    addressDimension = addressDimension / 8;
    }*/
    /**
     * Calculates the dimension of an adress.
     * It is based on the knowledge of the partitionDimension and blockDimension
     */
    void calculateAddressDimension() {
        int byteForPartition = getNearestExponentOfTwo(partitionDimension
                * Math.pow(2, partitionExponent));
        int byteForBlock = getNearestExponentOfTwo(blockDimension
                * Math.pow(2, blockExponent));

        addressDimension = byteForPartition - byteForBlock;
        addressDimension = getNearestTwoPow(addressDimension);
        //per ip almeno 8, voglio che addressDimension sia espresso in Byte
        if (addressDimension < 8) {
            addressDimension = 8;
        } else {
            addressDimension = addressDimension / 8;
        }
    }

    /**
     * Returns the major nearest power of two of the passed parameter
     * @param address
     * @return int
     */
    public static int getNearestTwoPow(int address) {
        int next = (int) Math.pow(2, Math.ceil(Math.log(address) / Math.log(2)));
        return next;
    }

    /**
     * a cosa devo elevare due per ottenere il numero ricevuto
     * @param address
     * @return
     */
    public static int getNearestExponentOfTwo(double address) {
        int next = (int) Math.ceil(Math.log(address) / Math.log(2));
        return next;
    }

    /**
     * Returns the maximum number of blocks used on the basis of disc size and
     * block size
     * @return int
     */
    int calculateBlockNumber() {
        return (int) ((partitionDimension * Math.pow(2, partitionExponent))
                / (blockDimension * Math.pow(2, blockExponent)));
    }

    /**
     * calculate average internal fragmentation of files in the simulation
     * @return
     */
    double fragAverage() {
        double totalFrag = 0;

        for (int i = 0; i < files.size(); i++) {
            totalFrag = totalFrag + calculateFileFragmentation((long) (files.elementAt(i).getDimension() * Math.pow(2,
                    files.elementAt(i).getExponent())));
        }

        return totalFrag;
    }

    /**
     * calculate internal fragmantation of selected file
     * @param sizeFile
     * @return internal fragmentation of a file
     */
    long calculateFileFragmentation(long sizeFile) {
        //il file occupa un numero intero di blocchi?
        long fragmentedSpace = sizeFile % (blockDimension
                * (long) Math.pow(2, blockExponent));
        return fragmentedSpace;
    }

    /**
     * Method which implemented allocation policy First Fit
     * @param item A section of file that will be inserted into usedBlocks
     */
    void firstFit(Section item) {
        boolean end = false;
        long firstSpace = 0;
        long spaceLenght = 0;
        if (usedBlocks.size() > 0) {

            spaceLenght = usedBlocks.firstElement().getFirst();
            if (spaceLenght >= item.getLenght()) {
                item.setFirst(firstSpace);
                end = true;
                usedBlocks.add(0, item);
            }
        } else {
            end = true;
            usedBlocks.add(0, item);
        }

        for (int index = 1; index < usedBlocks.size() && end != true; index++) {
            firstSpace = usedBlocks.elementAt(index - 1).getFirst()
                    + usedBlocks.elementAt(index - 1).getLenght();
            spaceLenght = usedBlocks.elementAt(index).getFirst() - firstSpace;
            if (spaceLenght >= item.getLenght()) {
                item.setFirst(firstSpace);
                end = true;
                usedBlocks.add(index, item);
            }
        }
        if (end == false) {
            firstSpace = usedBlocks.lastElement().getFirst()
                    + usedBlocks.lastElement().getLenght();
            item.setFirst(firstSpace);
            usedBlocks.addElement(item);/*inserito alla fine*/
        }
    }

    /**
     * Method which implemented allocation policy Best Fit
     * @param item A section of file that will be inserted into usedBlocks
     */
    void bestFit(Section item) {
        boolean end = false;
        long spaceNedded = item.getLenght();
        long bestStartSpace = calculateBlockNumber();
        long bestSpace = calculateBlockNumber();
        int bestIndex = -1;
        long firstSpace = usedBlocks.firstElement().getFirst();
        long lastStartSpace = usedBlocks.lastElement().getFirst() + usedBlocks.lastElement().getLenght();
        long lastSpace = calculateBlockNumber() - lastStartSpace;
        if (firstSpace >= spaceNedded) {
            bestStartSpace = 0;
            bestSpace = firstSpace;
            bestIndex = 0;
        }
        if (firstSpace != lastSpace && lastSpace >= spaceNedded && lastSpace <= bestSpace) {
            bestStartSpace = lastStartSpace;
            bestSpace = lastSpace;
            bestIndex = usedBlocks.size();
        }
        for (int index = 1; index < usedBlocks.size() && end != true; index++) {
            long startSpace = usedBlocks.elementAt(index - 1).getFirst()
                    + usedBlocks.elementAt(index - 1).getLenght();
            long spaceLenght = usedBlocks.elementAt(index).getFirst() - startSpace;
            if (spaceLenght >= spaceNedded && spaceLenght < bestSpace) {
                bestStartSpace = startSpace;
                bestSpace = spaceLenght;
                bestIndex = index;
            }
            if (spaceLenght == spaceNedded) {
                end = true;
            }
        }
        item.setFirst(bestStartSpace);
        usedBlocks.add(bestIndex, item);
    }

    /**
     * Method which implemented allocation policy Worst Fit
     * @param item A section of file that will be inserted into usedBlocks
     */
    void worstFit(Section item) {
        boolean end = false;
        long spaceNedded = item.getLenght();
        long worstStartSpace = calculateBlockNumber();
        long worstSpace = calculateBlockNumber();
        int worstIndex = -1;
        long firstSpace = usedBlocks.firstElement().getFirst();
        long lastStartSpace = usedBlocks.lastElement().getFirst() + usedBlocks.lastElement().getLenght();
        long lastSpace = calculateBlockNumber() - lastStartSpace;
        if (firstSpace >= spaceNedded) {
            worstStartSpace = 0;
            worstSpace = firstSpace;
            worstIndex = 0;
        }
        if (firstSpace != lastSpace && lastSpace >= spaceNedded && lastSpace >= worstSpace) {
            worstStartSpace = lastStartSpace;
            worstSpace = lastSpace;
            worstIndex = usedBlocks.size();
        }
        for (int index = 1; index < usedBlocks.size() && end != true; index++) {
            long startSpace = usedBlocks.elementAt(index - 1).getFirst()
                    + usedBlocks.elementAt(index - 1).getLenght();
            long spaceLenght = usedBlocks.elementAt(index).getFirst() - startSpace;
            if (spaceLenght >= spaceNedded && spaceLenght > worstSpace) {
                worstStartSpace = startSpace;
                worstSpace = spaceLenght;
                worstIndex = index;
            }
            if (spaceLenght == spaceNedded) {
                end = true;
            }
        }
        item.setFirst(worstStartSpace);
        usedBlocks.add(worstIndex, item);
    }

    /**
     * Method which implemented allocation policy Next Fit
     * @param item A section of file that will be inserted into usedBlocks
     */
    void nextFit(Section item) {
//        int maxElement = usedBlocks.size();
        boolean end = false;
//        long rightSpace = 0;
//        long rightStartSpace = 0;
        long firstLenght = 0;
        long firstSpace = usedBlocks.firstElement().getFirst();
        int rightIndex = allocationNextIndex;
        int nextIndex = allocationNextIndex;
        long spaceNedded = item.getLenght();
        if (allocationNextIndex == 0) {
            firstSpace = usedBlocks.firstElement().getFirst();
            firstLenght = firstSpace - 0;
            if (firstLenght >= spaceNedded) {
                item.setFirst(0);
                end = true;
                rightIndex = 0;
            } else {
                nextIndex = 1;
            }
        }
        for (int index = nextIndex; index < usedBlocks.size() && end != true; index++) {
            long startSpace = usedBlocks.elementAt(index - 1).getFirst()
                    + usedBlocks.elementAt(index - 1).getLenght();
            long spaceLenght = usedBlocks.elementAt(index).getFirst() - startSpace;
            if (spaceLenght >= spaceNedded) {
                item.setFirst(startSpace);
                rightIndex = index;
                end = true;
            }
        }
        long lastStartSpace = usedBlocks.lastElement().getFirst()
                + usedBlocks.lastElement().getLenght();
        long lastSpace = calculateBlockNumber() - lastStartSpace;
        if (lastSpace >= spaceNedded && end != true) {
            item.setFirst(lastStartSpace);
            end = true;
        }
        if (allocationNextIndex != 0) {
            firstSpace = usedBlocks.firstElement().getFirst();
            firstLenght = firstSpace - 0;
            if (firstLenght >= spaceNedded && end != true) {
                item.setFirst(0);
                end = true;
                rightIndex = 0;
            }
            for (int index = 1; index < nextIndex && end != true; index++) {
                long startSpace = usedBlocks.elementAt(index - 1).getFirst()
                        + usedBlocks.elementAt(index - 1).getLenght();
                long spaceLenght = usedBlocks.elementAt(index).getFirst() - startSpace;
                if (spaceLenght >= spaceNedded) {
                    item.setFirst(startSpace);
                    rightIndex = index;
                    end = true;
                }
            }
        }
        allocationNextIndex = rightIndex;
        usedBlocks.add(rightIndex, item);
    }

    /**
     * Methot that find a free space betwen parameter first and last that
     * can contain a section with dimension equals to spaceNedded
     * @param first
     * @param last
     * @param spaceNedded
     * @return
     */
    long findSpace(int first, int last, int spaceNedded) {
        long rightValue = 0;
        if (first == 0) {
            first = 1;
        }
        for (int index = first; index < last; index++) {
            long startSpace = usedBlocks.elementAt(index - 1).getFirst()
                    + usedBlocks.elementAt(index - 1).getLenght();
            long spaceLenght = usedBlocks.elementAt(index).getFirst() - startSpace;
            if (spaceLenght >= spaceNedded) {
                rightValue = startSpace;
            }
        }
        return rightValue;
    }

    /**
     * Method that calculates the reading time of a select file
     * @param itemSize - the size of file you want to know the reading time
     * @return a double containing the time reading
     */
    double readingTime() {/*ATTENZIONE GB MB KB...*/
        double itemSize = 0;
        boolean found = false;
        int i;
        for (i = 0; i < files.size() && !found; i++) {
            itemSize = itemSize + files.elementAt(i).getSize();
        }

        double rpsTime = (60.0 / rotationSpeed) * 1000;/*millisecondi*/
        double latency = rpsTime / 2;
        double readTime = rpsTime / trackPercent;
        double fileSector = itemSize
                / (int) (blockDimension * Math.pow(2, blockExponent));
        return (latency + seekTime + readTime) * fileSector;
    }

    /**
     * Returns the address dimension
     * @return addressDimension
     */
    public int getAddressDimension() {
        return addressDimension;
    }

    /**
     * Sets the address dimension
     * @param addressDimension
     */
    void setAddressDimension(int addressDimension) {
        this.addressDimension = addressDimension;
    }

    /**
     * Sets the name of a simulation
     * @param parName
     */
    void setSimName(String parName) {
        simName = parName;
    }

    /**
     * Returns the name of a simualtion
     * @return String
     */
    String getSimName() {
        return simName;
    }

    /**
     * Sets the dimension of a Partition
     * @param parPartition
     */
    void setPartitionDimension(int parPartition) {
        partitionDimension = parPartition;
    }

    /**
     * Return the dimension of a Partition
     * @return int
     */
    int getPartitionDimension() {
        return partitionDimension;
    }

    /**
     * Sets the dimension of a Block
     * @param parBlock
     */
    void setBlockDimension(int parBlock) {
        blockDimension = parBlock;
    }

    /**
     * Return the dimension of a Block
     * @return int
     */
    int getBlockDimension() {
        return blockDimension;
    }

    /**
     * Returns the exponent of the block in base two (example: KB = 10)
     * @return int
     */
    int getBlockExponent() {
        return blockExponent;
    }

    /**
     * Sets the exponent of the block in base two (example: KB = 10)
     * @param parBlockExponent
     */
    void setBlockExponent(int parBlockExponent) {
        blockExponent = parBlockExponent;
    }

    /**
     * Returns the exponent of the partition in base two (example: MB = 20)
     * @return
     */
    int getPartitionExponent() {
        return partitionExponent;
    }

    /**
     * Sets the exponent of the partition in base two (example: MB = 20)
     * @param parPartitionExponent
     */
    void setPartitionExponent(int parPartitionExponent) {
        partitionExponent = parPartitionExponent;
    }

    /**
     * Sets the speed of rotation of the disc
     * @param parDiskRotation
     */
    void setRotationSpeed(int parDiskRotation) {
        rotationSpeed = parDiskRotation;
    }

    /**
     * Return the speed of rotation of the disc
     * @return int
     */
    int getRotationSpeed() {
        return rotationSpeed;
    }

    /**
     * Sets the percentage of times a block without being contiguous with the
     * previous still resides within the same track record
     * @param parTrackPercent
     */
    void setTrackPercent(int parTrackPercent) {
        trackPercent = parTrackPercent;
    }

    /**
     * Returns the percentage of times a block without being contiguous with
     * the previous still resides within the same track record
     * @return int
     */
    int getTrackPercent() {
        return trackPercent;
    }

    /**
     * Sets the average head moves between tracks of the disk
     * @param parTimePosition
     */
    void setSeekTime(int parTimePosition) {
        seekTime = parTimePosition;
    }

    /**
     * Returns the average head moves between tracks of the disk
     * @return int
     */
    int getSeekTime() {
        return seekTime;
    }

//    /**
//     * Return the percentage of times a block without being contiguous with the
//     * previous still resides within the same track record
//     * @return int
//     */
//    public int getTrackSectors() {
//        return trackSectors;
//    }
//    /**
//     * Sets the percentage of times a block without being contiguous with the
//     * previous still resides within the same track record
//     * @param parTrackSectors
//     */
//    public void setTrackSectors(int parTrackSectors) {
//        trackSectors = parTrackSectors;
//    }
    /**
     * Sets the maximum length of a file that you can put in the File System
     * @param parMaxLenghtFile
     */
    void setMaxLenghtFile(long parMaxLenghtFile) {
        maxLenghtFile = parMaxLenghtFile;
    }

    /**
     * Returns the maximum length of a file that you can put in the File System
     * @return max file values
     */
    long getMaxLenghtFile() {
        return maxLenghtFile;
    }

    /**
     * Sets internal fragmentation
     * @param parInternalFragmentation
     */
    void setInternalFragmentation(double parInternalFragmentation) {
        internalFragmentation = parInternalFragmentation;
    }

    /**
     * Method that return internal fragmentation
     * @return return internal fragmentation
     */
    double getInternalFragmentation() {
        return internalFragmentation;
    }

    /**
     * Returns inflaction index of simulation
     * @return inflaction index
     */
    double getInflationIndex() {
        return inflationIndex;
    }

    /**
     * Sets inflaction index of simulation
     * @param inflationIndex
     */
    void setInflationIndex(double inflationIndex) {
        this.inflationIndex = inflationIndex;
    }

    /**
     * Sets the average time of reading a file
     * @param parReading
     */
    void setAverageReading(int parReading) {
        averageReading = parReading;
    }

    /**
     * Returns the average time of reading a file
     * @return int
     */
    int getAverageReading() {
        return averageReading;
    }

    /**
     * Sets if after opening or creating the data fields have been modified
     * @param parCamps
     */
    void setModifiedCamps(boolean parCamps) {
        modifiedCamps = parCamps;
    }

    /**
     * Return if after opening or creating the data fields have been modified
     * @return boolean
     */
    boolean getModifiedCamps() {
        return modifiedCamps;
    }

//    /**
//     * Sets if you have to recalculate the results of silmulazione to make them
//     * consistent with new data fields
//     * @param parCalculated
//     */
//    public void setJustCalculated(boolean parCalculated) {
//        justCalculated = parCalculated;
//    }
//
//    /**
//     * Return if you have to recalculate the results of silmulazione to make
//     * them consistent with new data fields
//     * @return boolean
//     */
//    public boolean getJustCalculated() {
//        return justCalculated;
//    }
    /**
     * Returns the index of the last block used by a partition file in the file
     * system (used for the allocation policy Next Fit)
     * @return int
     */
    int getAllocationNextIndex() {
        return allocationNextIndex;
    }

    /**
     * Sets the index of the last block used by a partition file in the file
     * system (used for the allocation policy Next Fit)
     * @param parNextIndex
     */
    void setAllocationNextIndex(int parNextIndex) {
        allocationNextIndex = parNextIndex;
    }

    /**
     * Returns the desired type of file allocation
     * @return int
     */
    int getFileAllocationType() {
        return fileAllocationType;
    }

    /**
     * Set the desired type of file allocation
     * @param parAllocationType
     */
    void setFileAllocationType(int parAllocationType) {
        fileAllocationType = parAllocationType;
    }

    /**
     * Returns the vector representing the used blocks in this simulation
     * @return Vector<Section>
     */
    Vector<Section> getUsedBlocks() {
        return usedBlocks;
    }

    /**
     * Method that return Vector of files
     * @return Vector of file in the simulation
     */
    public Vector<File> getFiles() {
        return files;
    }

    /**
     * Method that return selected file
     * @param id: id of file
     * @return selected file with any id
     */
    File getFile(String id) {
        boolean found = false;
        int index = -1;
        for (int i = 0; i < files.size() && !found; i++) {
            if (files.elementAt(i).getId().compareTo(id) == 0) {
                found = true;
                index = i;
            }
        }
        if (index == -1) {
            return null;
        } else {
            return files.elementAt(index);
        }
    }

    /**
     * Sets Vector of Files
     * @param files
     */
    void setFiles(Vector<File> files) {
        this.files = files;
    }

    /**
     * Sets Vector of Section
     * @param usedBlock
     */
    void setUsedBlocks(Vector<Section> usedBlocks) {
        this.usedBlocks = usedBlocks;
    }

    /**
     * returns variable chooseAdvanced
     * @return chooseAdvanced
     */
    boolean getChooseAdvanced() {
        return chooseAdvanced;
    }

    /**
     * Sets variable chooseAdvanced
     * @param chooseAdvanced
     */
    void setChooseAdvanced(boolean chooseAdvanced) {
        this.chooseAdvanced = chooseAdvanced;
    }

    /**
     * returns size of file system calculated
     * @return size of file system
     */
    double getSizeFileSystem() {
        return sizeFileSystem;
    }

    /**
     * sets ize of file system
     * @param sizeFileSystem
     */
    void setSizeFileSystem(double sizeFileSystem) {
        this.sizeFileSystem = sizeFileSystem;
    }

    /**
     * returns the configuration values of simulation
     * @return a Vector with config values
     */
    public Vector<String[]> getConfigurationValues() {
        Vector<String[]> config = new Vector<String[]>();

        config.add(new String[]{Model.translate("blockDimension"),
                    String.valueOf(blockDimension) + " "
                    + exponentToString(blockExponent)});
        config.add(new String[]{Model.translate("diskDimension"),
                    String.valueOf(partitionDimension) + " "
                    + exponentToString(partitionExponent)});
        if (chooseAdvanced) {
            config.add(new String[]{Model.translate("seekTime"),
                        String.valueOf(seekTime) + " ms"});
            config.add(new String[]{Model.translate("trackDimension"),
                        String.valueOf(trackPercent) + " "
                        + Model.translate("blocks")});
            config.add(new String[]{Model.translate("rotationSpeed"),
                        String.valueOf(rotationSpeed)});
        }

        return config;
    }

    /**
     * return a Vector with results values
     * @return values of results
     */
    abstract public Vector<String[]> getResultsValues();

    /**
     * convert number of exponent into String
     * @param exponent
     * @return Exponent dimension: Byte, KB, MB, GB
     */
    String exponentToString(int exponent) {
        String value = "";

        switch (exponent) {
            case 0: {
                value = "Byte";
                break;
            }
            case 10: {
                value = "KB";
                break;
            }
            case 20: {
                value = "MB";
                break;
            }
            case 30: {
                value = "GB";
                break;
            }
            case 40: {
                value = "TB";
                break;
            }
        }

        return value;
    }
}
